package com.elaunira.rdf.datasets.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import com.elaunira.rdf.datasets.util.math.CubicSpline;

/**
 * Class for performing an interpolation of the BSBM scale factor using a cubic
 * spline. Depending on the scale factor value, BSBM will generate a number of
 * triples. With this class you can approximate the scale factor to use in order
 * to get the desired number of triples.
 * 
 * @author Laurent Pellegrino
 * 
 * @version $Id: BsbmInterpolation.java 4 2010-12-22 19:14:25Z laurent.pellegrino $
 */
public class BsbmInterpolation {

	private CubicSpline spline;

	private InputStream dataFileInputStream;

	public static BsbmInterpolation instance;
	
	public BsbmInterpolation(File fileForInterpolation) {
		if (fileForInterpolation != null) {
			try {
				this.dataFileInputStream = new FileInputStream(fileForInterpolation);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		} else {
			this.dataFileInputStream = this.getClass()
					.getResourceAsStream("/bsbm-data-interpolation");
		}
		
		this.loadDataForInterpolation(this.dataFileInputStream);
	}
	
	private void loadDataForInterpolation(InputStream stream) {
		List<Double> xValues = new ArrayList<Double>();
		List<Double> yValues = new ArrayList<Double>();

		try {
			BufferedReader br = 
				new BufferedReader(
						new InputStreamReader(
								this.dataFileInputStream));
			
			String line;
			String[] lineSplit;
			while ((line = br.readLine()) != null) {
				lineSplit = line.split("\t");
				xValues.add(Double.parseDouble(lineSplit[0]));
				yValues.add(Double.parseDouble(lineSplit[1]));
			}

			this.spline = new CubicSpline(xValues, yValues);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public int computeScaleFactor(int nbTriples) {
		return 
			(int) Math.round(
					this.spline.interpolate(nbTriples));
	}
	
	public InputStream getDataFileInputStream() {
		return this.dataFileInputStream;
	}

	public static BsbmInterpolation getInstance() {
		return getInstance(null);
	}
	
	public static BsbmInterpolation getInstance(File fileForInterpolation) {
		try {
			if (instance == null ||
						!instance.getDataFileInputStream().equals(
								new FileInputStream(fileForInterpolation))) {
				instance = new BsbmInterpolation(fileForInterpolation);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		return instance;
	}
	
	public static void main(String[] args) {
		if (args.length != 1) {
			throw new IllegalArgumentException("Number of triples expected!");
		}
		
		try {
			System.out.println(
					BsbmInterpolation.getInstance()
						.computeScaleFactor(Integer.parseInt(args[0])));
		} catch (NumberFormatException e) {
			throw new IllegalArgumentException(args[0] + " is not an Integer!");
		}
	}
	
}
